
#include "stdafx.h"
#include <JeayeSDK/JJson.h>
#include <JeayeSDK/CharFun.h>
#include <wchar.h>
#include <math.h>

#ifdef _DEBUG
#define new new(__FILE__, __LINE__)
#endif
/////////////////////////////////////////////////////////////////
//json for char version
/////////////////////////////////////////////////////////////////
JJsonA::JJsonA()
{
}

JJsonA::~JJsonA()
{
	destroy();
}

BOOL JJsonA::parse_json(const char * data)
{
	_ASSERT(data != NULL);
	if (data == NULL)
		return FALSE;

	json_read(myStrTrait::skip_s(data));

	return TRUE;
}
const char * JJsonA::json_read(const char * s)
{
	stStringBlockInfoA block = { 0 };

	json_type = C_JSON_NULL;
	/* Build an object from input text. */
	if (OBJECT_START(*s)) {
		json_type = C_JSON_OBJECT;
		object.c_object = hmap_create(TABLE_INIT_SIZE, NULL, NULL);

		s = StringTrait<char>::skip_s(++s);
		if (s) {
			/* empty object. */
			if (OBJECT_END(*s)) {
				return StringTrait<char>::skip_s(++s);
			}

			do {
				s = read_key_and_value(s);
				if (s && NEXT_VALUE(*s)) {
					s = StringTrait<char>::skip_s(++s);
				}
				else {
					break;
				}
			} while (s);

			if (s && OBJECT_END(*s)) {
				return StringTrait<char>::skip_s(s + 1);
			}
		}

		return NULL;
	}

	/* Build an array from input text. */
	if (ARRAY_START(*s)) {
		json_type = C_JSON_ARRAY;
		object.c_array = new JArray(ARRAY_INIT_SIZE);

		s = StringTrait<char>::skip_s(++s);
		if (s) {
			/* empty array. */
			if (ARRAY_END(*s)) {
				return StringTrait<char>::skip_s(++s);
			}

			do {
				s =json_read_array_value(s, this);
				if (s && NEXT_VALUE(*s)) {
					s = StringTrait<char>::skip_s(++s);
				}
				else {
					break;
				}
			} while (s);

			if (s && ARRAY_END(*s)) {
				return StringTrait<char>::skip_s(s + 1);
			}
		}

		return NULL;
	}

	/*string*/
	s = json_read_string(s, 0, &block);
	if (block.finded) {
		char *cpys = NULL;
		json_type = C_JSON_STRING;
		object.c_string = C_MALLOCX(char, (block.count + 1));
		cpys = object.c_string;
		while (block.start_str != block.end_str) {
			if (*block.start_str != '\\')
				*cpys++ = *block.start_str++;
			else {
				block.start_str++;
				switch (*block.start_str)
				{
				case 'b': *cpys++ = '\b'; block.start_str++; break;
				case 'f': *cpys++ = '\f'; block.start_str++; break;
				case 'n': *cpys++ = '\n'; block.start_str++; break;
				case 'r': *cpys++ = '\r'; block.start_str++; break;
				case 't': *cpys++ = '\t'; block.start_str++; break;
				case '\"':
				case '\\':
				case '/': *cpys++ = *block.start_str++; break;
				default:  *cpys++ = '\\'; *cpys++ = *block.start_str++; break;
				}
			}
		}
		*cpys = 0;
		//
		return s;
	}

	if (*s == '-' || isdigit(*s)) {
		return myTrial::read_number(s,this);
	}

	if (_strnicmp(s, "true", 4) == 0) {
		json_type = C_JSON_BOOLEAN;
		object.c_boolean = TRUE;
		s += 4;
	}
	else if (_strnicmp(s, "false", 5) == 0) {
		json_type = C_JSON_BOOLEAN;
		object.c_boolean = FALSE;
		s += 5;
	}
	else if (_strnicmp(s, "null", 4) == 0) {
		json_type = C_JSON_NULL;
		object.c_boolean = FALSE;
		s += 4;
	}

	return StringTrait<char>::skip_s(s);
}

JJsonA * JJsonA::create_json_object(BYTE json_type, const char * key_value)
{
	JJsonA *j = (JJsonA*)c_malloc(sizeof(JJsonA));
	if (j == NULL) {
		return NULL;
	}

	ZeroMemory(j, sizeof(JJsonA));

	j->json_type = json_type;
	if (j->json_type == C_JSON_OBJECT) {
		j->object.c_object = hmap_create(TABLE_INIT_SIZE, NULL, NULL);
		if (j->object.c_object == NULL) {
			c_free(j);
			return NULL;
		}
	}
	else if (j->json_type == C_JSON_ARRAY) {
		j->object.c_array = new JArray(ARRAY_INIT_SIZE);
		if (j->object.c_array == NULL) {
			c_free(j);
			return NULL;
		}
	}

	if (key_value)
		j->key_value = c_acsdup(key_value);
	else
		j->key_value = NULL;

	return j;
}

JJsonA * JJsonA::create_json_string(const char * key_value, const char * value_data)
{
	JJsonA *j = create_json_object(C_JSON_STRING, key_value);
	if (j == NULL) return NULL;
	j->object.c_string = c_acsdup(value_data);
	return j;
}

BOOL JJsonA::append_json_object( JJsonA * j)
{
	JVariant var;
	if ( j == NULL) {
		return FALSE;
	}

	if (json_type == C_JSON_OBJECT) 
	{
		var.set(j->key_value);
		if (hmap_insert(object.c_object, var.get_variant(), j) == FALSE) {
			c_free(j);
			return FALSE;
		}
	}
	else if (json_type == C_JSON_ARRAY) {
		object.c_array->push(j);
	}
	return TRUE;
}

JJsonA * JJsonA::FindObject(const char * key_name)
{
	JVariant var;
	
	if (json_type != C_JSON_OBJECT) {
		return NULL;
	}
	var.set(key_name);
	return (JJsonA*)hmap_find(object.c_object, var.get_variant());
}

void JJsonA::save_json_file(FILE * fp)
{
	if (fp == NULL) {
		return;
	}
	save_json_value_file(fp);
}

BOOL JJsonA::destroy_json_object(JJsonA * json)
{
	if (json->json_type == C_JSON_OBJECT)
	{
		if (json->object.c_object == 0) return TRUE;
		double_iterator* __NEXT = ((double_link*)hmap_link(json->object.c_object))->first();
		JJsonA *obj = NULL;
		c_variant_t *key = NULL;
		while (NULL != __NEXT)
		{
			obj = (JJsonA*)hmap_value(__NEXT, &key);
			__NEXT = __NEXT->m_next;
			switch (obj->json_type) {
			case C_JSON_OBJECT:
				destroy_json_object(obj);
				break;
			case C_JSON_STRING:
				c_free(obj->object.c_string);
				break;
			case C_JSON_ARRAY:
				destroy_json_array(obj);
				break;
			}

			if (obj->key_value) {
				c_free(obj->key_value);
			}
			c_free(obj);
		}

		hmap_destroy(&json->object.c_object);
		json->object.c_object = 0;
		return TRUE;
	}
	return FALSE;
}

BOOL JJsonA::destroy_json_array(JJsonA * json)
{
	if (json->json_type == C_JSON_ARRAY)
	{
		if (json->object.c_array == 0) return TRUE;
		JJsonA* obj = NULL;
		UINT32 __COUNT = json->object.c_array->get_count(), key = 0;
		for (key = 0; key < __COUNT; key++)
		{
			obj = (JJsonA*)json->object.c_array->find(key);
			switch (obj->json_type) {
			case C_JSON_OBJECT:
				destroy_json_object(obj);
				break;
			case C_JSON_STRING:
				c_free(obj->object.c_string);
				break;
			case C_JSON_ARRAY:
				destroy_json_array(obj);
				break;
			}
			c_free(obj);
		}

		delete json->object.c_array;
		json->object.c_array = 0;
		return TRUE;
	}

	return FALSE;
}

void JJsonA::destroy()
{
	if (destroy_json_object(this)) {
		return;
	}

	if (destroy_json_array(this)) {
		return;
	}

	return;
}

const char* JJsonA::read_key_and_value(const char *s)
{
	JVariant var;
	stStringBlockInfoA block = { 0 };
	JJsonA *new_json = NULL;

	s = json_read_string(s, ':', &block);
	if (s == NULL) {
		return NULL;
	}
	else if (*s != ':') {
		return NULL;
	}

	new_json = C_CALLOC(JJsonA, 1);
	if (new_json == NULL)
		return NULL;

	if (block.finded) {
		new_json->key_value = C_MALLOCX(char, (block.count + 1));
		strncpy(new_json->key_value, block.start_str, block.count);
		new_json->key_value[block.count] = '\0';
	}
	/* skip++ ':' */
	s = new_json->json_read(StringTrait<char>::skip_s(++s));

	var.set(new_json->key_value);
	if (hmap_insert(object.c_object, var.get_variant(), new_json) == FALSE) {
		c_free(new_json);
		return NULL;
	}

	return s;
}

const char* JJsonA::json_read_string(const char *s, char end_flag, stStringBlockInfoA *block)
{
	if (block == NULL) {
		return NULL;
	}

	block->finded = FALSE;
	block->count = 0;
	block->start_str = NULL;
	block->end_str = NULL;
	if (*s == '\'') {
		block->finded = TRUE;
		block->start_str = ++s;
		while (s) {
			if (*(s - 1) != '\\' && *s == '\'') {
				block->end_str = s;
				s++;
				break;
			}
			block->end_str = ++s;
		}
	}
	else if (*s == '"') {
		block->finded = TRUE;
		block->start_str = ++s;
		while (s) {
			if (*(s - 1) != '\\' && *s == '"') {
				block->end_str = s;
				s++;
				break;
			}
			block->end_str = ++s;
		}
	}
	else if (end_flag) {
		block->finded = TRUE;
		block->start_str = s;
		while (s && !c_is_space(*s)) {
			if (*s == end_flag) {
				block->end_str = s;
				break;
			}
			block->end_str = ++s;
		}
	}

	if (block->end_str) {
		block->count = (int)(block->end_str - block->start_str);
	}

	return myStrTrait::skip_s(s);
}

void JJsonA::save_json_value_file(FILE * fp)
{
	switch (this->json_type)
	{
	case C_JSON_OBJECT: {
		save_json_object_file(fp);
	}
						break;
	case C_JSON_BOOLEAN: {
		fprintf(fp, "%s", this->object.c_boolean == TRUE ? "true" : "false");
		break;
	}
	case C_JSON_DOUBLE: {
		fprintf(fp, "%f", this->object.c_double);
		break;
	}
	case C_JSON_STRING: {
		if (this->object.c_string == NULL)
			fputs("\"\"", fp);
		else
			fprintf(fp, "\"%s\"", this->object.c_string);
		break;
	}
	case C_JSON_INT: {
		fprintf(fp, "%lld", (long long)this->object.c_int);
		break;
	}
	case C_JSON_ARRAY: {
		//_tprintf(_T("\n--object print(array)--\n"));
		this->save_json_array_file(fp);
		break;
	}
	case C_JSON_NULL: {
		fputs("null", fp);
		break;
	}
	default:
		//_tprintf(_T("error type %d\n"), js_obj->json_type);
		break;
	}
}

void JJsonA::save_json_array_file(FILE * fp)
{
	UINT count = this->object.c_array->get_count() - 1;
	fputc(_T('['), fp);
	JJsonA* js_obj = NULL;
	UINT32 __COUNT = this->object.c_array->get_count(), key = 0;
	for (key = 0; key < __COUNT; key++)
	{
		js_obj = (JJsonA*)this->object.c_array->find(key);
		if (js_obj == NULL) continue;
		js_obj->save_json_value_file(fp);
		if (key < count)
			fputc(_T(','), fp);
	}
	fputc(_T(']'), fp);
}

void JJsonA::save_json_object_file(FILE * fp)
{
	double_iterator* _next = ((double_link*)hmap_link(this->object.c_object))->first();
	JJsonA *js_obj = NULL;
	c_variant_t *key = NULL;

	fputc(_T('{'), fp);
	while (NULL != _next) {
		js_obj = (JJsonA*)hmap_value(_next, &key);
		_next = _next->m_next;

		if (js_obj == NULL) continue;
		if (js_obj->key_value) {
			fprintf(fp, "\"%s\":", js_obj->key_value);
		}

		this->save_json_value_file(fp);

		if (_next != NULL) {
			fputc(_T(','), fp);
		}
	}
	fputc(_T('}'), fp);
}
